package com.lge.clock.worldclock.map;

import java.util.ArrayList;
import java.lang.Float;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

//import android.app.ActivityManagerNative;
//import android.app.IActivityManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.view.GestureDetector;
import android.view.MotionEvent;

import com.lge.clock.AlarmClockActivity;
import com.lge.clock.R;
import com.lge.clock.util.CommonUtil;
import com.lge.clock.worldclock.MapSearchActivity;
import com.lge.clock.worldclock.cities.CityInfoDisplay;
import com.lge.clock.worldclock.cities.CityInformation;
import com.lge.clock.worldclock.util.DataManager;
import com.lge.clock.worldclock.util.Log;

/**
 * @author lge.com
 */
public class EarthRenderer implements GLSurfaceView.Renderer {
    private static final String PREFIX = "EarthRenderer";
    
    private static final float TOUCH_MOVE_FACTOR = 180.0f / 900.0f;
    private static final float TOUCH_SCALE_FACTOR = 500.0f;

    private static final float INIT_SHAPE_ROTATE_X_COORDINATE = -90.0f;
    private static final float INIT_CAMERA_LOACTION_Z_COORDINATE = 10.0f;
    private static final float INIT_SHAPE_LOCATION_Z_COORDINATE = -4.0f;

    private static final float SHAPE_ROTATE_MINIMUM_ANGLE_LANDSCAPE = -85.0f;
    private static final float SHAPE_ROTATE_MINIMUM_ANGLE = -75.0f;
    private static final float SHAPE_ROTATE_MAXIMUM_ANGLE = 75.0f;

    public static final float SHAPE_SCALE_MINIMUM_SIZE = 1.0f;
    public static final float SHAPE_SCALE_MAXIMUM_SIZE = 2.5f;

    private static final float TOUCH_VALID_DISTANCE = 10.0f;

    private static final float DRAW_TEXT_SIZE = 18;
    private static final float DRAW_TEXT_SIZE_XDPI = 21;
    private static final float DRAW_TEXT_SIZE_MDPI = 12;
    private static final int   DRAW_TEXT_COLOR = 0xFF000000; //0xFFFF0000;
    
    private static final float LABEL_LOCATION_ERROR_VALUE_X = 10.0f;
    private static final float LABEL_LOCATION_ERROR_VALUE_Y = -10.0f;
    
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_1 = 1.3f;
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_2 = 1.5f;
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_3 = 1.7f;
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_4 = 1.9f;
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_5 = 2.1f;
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_6 = 2.3f;
    public static final float DISPLAY_SHAPE_SCALE_LEVEL_7 = 2.5f;
    
    public static final int CITY_DISPLAY_LEVEL_NONE = 0;
    public static final int CITY_DISPLAY_LEVEL_1 = 1;
    public static final int CITY_DISPLAY_LEVEL_2 = 2;
    public static final int CITY_DISPLAY_LEVEL_3 = 3;
    public static final int CITY_DISPLAY_LEVEL_4 = 4;
    public static final int CITY_DISPLAY_LEVEL_5 = 5;
    public static final int CITY_DISPLAY_LEVEL_6 = 6;
    public static final int CITY_DISPLAY_LEVEL_7 = 7;
    
    private static final float DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_1 = 0.9667f;
    private static final float DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_2 = 0.9659f;
    private static final float DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_3 = 0.9643f;
    private static final float DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_4 = 0.9580f;
    private static final float DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_5 = 0.9550f;

    private static final int ZOOM_IN_STATUS = 0;
    private static final int ZOOM_OUT_STATUS = 1;
    
    private static final float EARTH_SIZE_LEVEL_0 = SHAPE_SCALE_MINIMUM_SIZE;
    private static final float EARTH_SIZE_LEVEL_1 = (SHAPE_SCALE_MINIMUM_SIZE+SHAPE_SCALE_MAXIMUM_SIZE)/2.0f;
    private static final float EARTH_SIZE_LEVEL_2 = SHAPE_SCALE_MAXIMUM_SIZE;
    
    private static final float SEOUL_ROTATE_X = 36.60f;
    private static final float SEOUL_ROTATE_Y = 52.92f;
    
    public static final int HANDLE_MESSAGE_TYPE_INIT_ROTATE = 1;
    public static final int HANDLE_MESSAGE_TYPE_ROTATE_ANIMATION = 2;
    public static final int HANDLE_MESSAGE_TYPE_LOAD_IMAGE = 3;
    
    public static final int ANIMATION_STEP_SIZE = 50;
    
    private Context mContext;
    private GLSurfaceView mGLSurfaceView;

    private int mViewWidth;
    private int mViewHeight;

    protected int mTextures[] = new int[1];

    private float mPreviousX = 0.0f;
    private float mPreviousY = 0.0f;
    private float mPreviousDistance = -1.0f;

    private ShapeGroup mShapeGroup;
    private EarthLines mEarthLines;
    
    private CityInfoDisplay mCityInfoDisplay;

    private Paint mLabelPaint;
    private LabelMaker mLabelMaker;
    private Projector mProjector;

    private GestureDetector mGestureDetector;

    private int[] mLabels;
    private float[] mScratch = new float[8];
    private float scaleArray[] = {
            EARTH_SIZE_LEVEL_0,
            EARTH_SIZE_LEVEL_1,
            EARTH_SIZE_LEVEL_2,
    };
    
    private static float sCurrentScaleValue = 1.0f;
    
    private static final int LOAD_REAL_IMAGE_PROCESS_STATE_NONE = 0;
    private static final int LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_START = 1;
    private static final int LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_LOADING = 2;
    private static final int LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_COMPLETE = 3;
    
    private int mImageLoadIndex = 0;
    private ArrayList<EarthImageInfo> mImageLoadInfoList = new ArrayList<EarthImageInfo>();

    private int[] mInitializeImageIDList = DataManager.earth_level0_image_resource_id_list;
    private int[] mImageResourceIDList = DataManager.earth_level1_image_resource_id_list;
    private String[] mEarthSphereNameList = DataManager.earth_gl_object_name_list;
    
    private static Bitmap sRealImageBitmap;
    
    private static float mTargetX = SEOUL_ROTATE_X;
    private static float mTargetY = SEOUL_ROTATE_Y;
    
    private boolean mIsDestroy = false;
    
    public EarthRenderer(Context context, GLSurfaceView glSurfaceView) {
        Log.message(PREFIX, "EarthRenderer() - S");
        mContext = context;
        mGLSurfaceView = glSurfaceView;

        initShapeSettings();
        initLabelSettings(context);
        gestureDetector();
        
        if (sRealImageBitmap != null) {
            Log.message(PREFIX, "EarthRenderer() image recycle");
            sRealImageBitmap.recycle();
            sRealImageBitmap = null;
        }
        
        Log.message(PREFIX, "EarthRenderer() - E");
    }

    public static float getCurrentScale() {
        return sCurrentScaleValue;
    }
    
    /** Make a earth size. */
    public float getCurrentZoomStatus()
    {
    	float earth_size = -1.0f;	
    	
    	//moon.dongchul@lge.com WBT #297444, #297445 2012-01-05
    	if((sCurrentScaleValue - EARTH_SIZE_LEVEL_0) < 0.001){
    		earth_size = EARTH_SIZE_LEVEL_0;
    	}
    	else if((sCurrentScaleValue - EARTH_SIZE_LEVEL_2) < 0.001)
    		{
    			earth_size = EARTH_SIZE_LEVEL_2;
    		}
    	earth_size = sCurrentScaleValue;
    	
    	return earth_size;
    }
    
    @SuppressWarnings("unused")
	private void initRotate() {
    	AnimationInfo aInfo = new AnimationInfo();
        float stepX = (mTargetX - mShapeGroup.getRotateX())/ANIMATION_STEP_SIZE; 
        float stepY = (mTargetY - mShapeGroup.getRotateY())/ANIMATION_STEP_SIZE;
        float stepScale = (sCurrentScaleValue - mShapeGroup.getScale())/ANIMATION_STEP_SIZE;
    	
    	aInfo.setStepX(stepX);
    	aInfo.setStepY(stepY);
    	aInfo.setStepScale(stepScale);
    	aInfo.setLoopCnt(ANIMATION_STEP_SIZE);
//    	Log.message(PREFIX, "initRotate()");
    	mHandler.obtainMessage(HANDLE_MESSAGE_TYPE_INIT_ROTATE, aInfo).sendToTarget();
	}

    private void initRotateSettings() {
    	mShapeGroup.setRotate(mTargetX, mTargetY, 0.0f);
    	mShapeGroup.setScale(sCurrentScaleValue);
    	processScale(0.0f);
    }
    
	private void initShapeSettings() {
        mShapeGroup = new ShapeGroup();
        
    	for(int i = 0; i < mImageResourceIDList.length; i++) {
    		EarthImageInfo imgInfo = new EarthImageInfo();
    		imgInfo.initializeImageID = mInitializeImageIDList[i];
    		imgInfo.imageResourceID = mImageResourceIDList[i];
    		imgInfo.earthSphere = new EarthSphere(mEarthSphereNameList[i]);
    		imgInfo.earthSphere.setColor(1.0f, 1.0f, 1.0f, 1.0f);
    		imgInfo.earthSphere.setTextureProcess(true);
    		mImageLoadInfoList.add(imgInfo);
    	}
        
        mEarthLines = new EarthLines();
        mEarthLines.setScale(1.002f);

    	for(EarthImageInfo imgInfo: mImageLoadInfoList) {
    		mShapeGroup.add(imgInfo.earthSphere);
    	}
        mShapeGroup.add(mEarthLines);

        mShapeGroup.setInitRotate(INIT_SHAPE_ROTATE_X_COORDINATE, 0, 0);
        mShapeGroup.setTranslate(0.0f, 0.0f, INIT_SHAPE_LOCATION_Z_COORDINATE);

        mShapeGroup.setTranslateProcess(true);
        mShapeGroup.setRotateProcess(true);
        mShapeGroup.setScaleProcess(true);
    }

    private void initLabelSettings(Context context) {
        mCityInfoDisplay = new CityInfoDisplay(context);
        
        mProjector = new Projector();
        mLabelPaint = new Paint();


        if(MapSearchActivity.bXhdpi){
        	mLabelPaint.setTextSize(DRAW_TEXT_SIZE_XDPI);
        } else if(MapSearchActivity.bMdpi){
    		mLabelPaint.setTextSize(DRAW_TEXT_SIZE_MDPI);
    	}else{
    		mLabelPaint.setTextSize(DRAW_TEXT_SIZE);
    	}
        
        
        mLabelPaint.setAntiAlias(true);
        mLabelPaint.setColor(DRAW_TEXT_COLOR);
    }

    public CityInfoDisplay getDisplayCityInfo() {
    	return mCityInfoDisplay;
    }
    
    private void gestureDetector() {
        mGestureDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                    float velocityY) {
                
                AnimationInfo aInfo = new AnimationInfo(velocityX, velocityY, ANIMATION_STEP_SIZE);
                startAnimating(aInfo);
                return false;
            }
        });
    }
    
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.message(PREFIX, "onSurfaceCreated() - S");
        gl.glDisable(GL10.GL_DITHER);

        // Set the background color to white ( rgba ).
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);  // OpenGL docs.
        // Enable Smooth Shading, default not really needed.
        gl.glShadeModel(GL10.GL_SMOOTH);// OpenGL docs.

        gl.glEnable(GL10.GL_TEXTURE_2D);

        // Depth buffer setup.
        gl.glClearDepthf(1.0f);// OpenGL docs.
        // Enables depth testing.
        gl.glEnable(GL10.GL_DEPTH_TEST);// OpenGL docs.
        // The type of depth testing to do.
        gl.glDepthFunc(GL10.GL_LEQUAL);// OpenGL docs.

        // Really nice perspective calculations.
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs.
                GL10.GL_NICEST);

        int i=0;
        for(EarthImageInfo imgInfo : mImageLoadInfoList) {
        	if (imgInfo.earthSphere.isTextureProcess()) {
        		imgInfo.earthSphere.setmBitmapArrNum(i++);
        		imgInfo.earthSphere.loadGLTexture(gl, mContext, imgInfo.initializeImageID);
        	}
        }
        
        createLabelMaker(gl);
        initEarthImageLoadStatus();
        initRotateSettings();
//        Log.message(PREFIX, "onSurfaceCreated() - E");
    }

	private void initEarthImageLoadStatus() {
		for(EarthImageInfo imgInfo : mImageLoadInfoList) {
			imgInfo.status = LOAD_REAL_IMAGE_PROCESS_STATE_NONE;
		}
		mImageLoadIndex = 0;
    }
    
	private void createLabelMaker(GL10 gl) {
    	int cityCount = mCityInfoDisplay.getCityCount();
    	Bitmap cityDisplayImage = BitmapFactory.decodeResource(mContext.getResources(),
    			R.drawable.worldclock_map_ctiy_point);
    	
    	if (mLabelMaker != null) {
    		mLabelMaker.shutdown(gl);
    	} else {
    		// [junehyuk.jang] 2011.08.19 Modify label maker START 
    		if(MapSearchActivity.bXhdpi)
    		    mLabelMaker = new LabelMaker(true, 1280, 1280);
    		else
    		    mLabelMaker = new LabelMaker(true, 1024, 1024);
    		// [junehyuk.jang] 2011.08.19 Modify label maker END
    	}
    	
    	mLabelMaker.initialize(gl);
    	mLabelMaker.beginAdding(gl);
    	mLabels = new int[cityCount];
    	for (int i=0 ; i < cityCount ; i++) {
    		CityInformation cityInfo = mCityInfoDisplay.getCityInformation(i);
    		int labelPosition = cityInfo.getLabelPosition();
			if (cityDisplayImage != null) //WBT_nullpointer
    		mLabels[i] = mLabelMaker.add(gl, null, cityInfo.getCityName(), mLabelPaint, cityDisplayImage, labelPosition);
    	}
    	if (cityDisplayImage != null) {
    		cityDisplayImage.recycle();
    		cityDisplayImage = null;
    	}
    	mLabelMaker.endAdding(gl);
	}

	public void onResume() {
		Log.message(PREFIX, "onResume()");
		initEarthImageLoadStatus();
	};
	
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.message(PREFIX, "onSurfaceChanged() - S");
    	int orientation = mContext.getResources().getConfiguration().orientation;

    	mViewHeight = height;

        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
        	height = DataManager.getSurfaceViewHeight();
        }

        mViewWidth = width;

        changeShapeAttribute(orientation);

        // Sets the current view port to the new size.
        gl.glViewport(0, 0, width, height);// OpenGL docs.

        mProjector.setCurrentView(0, 0, width, height);

        // Select the projection matrix
        gl.glMatrixMode(GL10.GL_PROJECTION);// OpenGL docs.
        // Reset the projection matrix
        gl.glLoadIdentity();// OpenGL docs.

        float winodwRatio = (float) width / (float) height;
        // Calculate the aspect ratio of the window
        GLU.gluPerspective(gl, 45.0f,
                winodwRatio,
                0.1f, 100.0f);

        // Label Maker setting.
        mProjector.currentProjection(gl);
        Log.message(PREFIX, "onSurfaceChanged() - S");
    }
    
    private void changeShapeAttribute(int orientation) {
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            mShapeGroup.setTranslate(0.0f, 0.0f, INIT_SHAPE_LOCATION_Z_COORDINATE);
            mShapeGroup.setScaleLimit(SHAPE_SCALE_MINIMUM_SIZE, SHAPE_SCALE_MAXIMUM_SIZE);
            mShapeGroup.setRoateLimit(Shape.SHAPE_ROTATE_COORDINATE_X, 
                    SHAPE_ROTATE_MINIMUM_ANGLE, 
                    SHAPE_ROTATE_MAXIMUM_ANGLE);
        } else { // orientation == Configuration.ORIENTATION_LANDSCAPE
            mShapeGroup.setTranslate(0.0f, DataManager.getShapeTranslateY(),
            											INIT_SHAPE_LOCATION_Z_COORDINATE);
            mShapeGroup.setScaleLimit(SHAPE_SCALE_MINIMUM_SIZE, SHAPE_SCALE_MAXIMUM_SIZE);
            mShapeGroup.setRoateLimit(Shape.SHAPE_ROTATE_COORDINATE_X, 
                    SHAPE_ROTATE_MINIMUM_ANGLE_LANDSCAPE, 
                    SHAPE_ROTATE_MAXIMUM_ANGLE);
        }
        
        mGLSurfaceView.requestRender();
    }

    public float getPointToPointDistance(float point1_x, float point1_y, float point2_x, float point2_y) {
        float width = Math.abs(point2_x - point1_x);
        float height = Math.abs(point2_y - point1_y);

        float distance = (float)Math.sqrt(width*width + height*height);
        return distance;
    }

    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureDetector != null) {
            if (mGestureDetector.onTouchEvent(event)) {
                return true;
            }
        }

        float x = event.getX();
        float y = event.getY();

        int action = event.getAction();
        int pointerCount = event.getPointerCount();
        if (pointerCount == 2) {
            // multi point process
            int actionPointId_1 = event.getPointerId(0);
            int actionPointId_2 = event.getPointerId(1);

            int pointerIndex_1 = event.findPointerIndex(actionPointId_1);
            int pointerIndex_2 = event.findPointerIndex(actionPointId_2);

            switch (action) {
            case MotionEvent.ACTION_MOVE: {
            	float currentDistance = getPointToPointDistance(event.getX(pointerIndex_1),
            			event.getY(pointerIndex_1),
            			event.getX(pointerIndex_2),
            			event.getY(pointerIndex_2));

            	Float scaleSize = 0.0f;

            	if (mPreviousDistance > 0.0f) {
            		scaleSize = (currentDistance - mPreviousDistance) / TOUCH_SCALE_FACTOR;
            	}

            	if (scaleSize.compareTo(0.0f) != 0) {    
            		processScale(scaleSize);
            	}

                mPreviousDistance = currentDistance;
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                mPreviousDistance = -1.0f;
                break;
            }
            default :
            }

            mPreviousX = -1.0f;
            mPreviousY = -1.0f;

        } else {
            // one point process
            switch (action) {
            case MotionEvent.ACTION_DOWN: {
                mPreviousX = x;
                mPreviousY = y;
                stopAnimating();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if ((x - TOUCH_VALID_DISTANCE) > mPreviousX || 
                        (x + TOUCH_VALID_DISTANCE) < mPreviousX ||
                        (y - TOUCH_VALID_DISTANCE) > mPreviousY || 
                        (y + TOUCH_VALID_DISTANCE) < mPreviousY) {
                    if (mPreviousX > 0.0f && mPreviousY > 0.0f) {
                        float dx = x - mPreviousX;
                        float dy = y - mPreviousY;

                        float shapeScaleSize = mShapeGroup.getScale();
                        float touchMoveFator = TOUCH_MOVE_FACTOR;

                        if (shapeScaleSize > 1.0f) {
                            touchMoveFator = touchMoveFator / shapeScaleSize;
                        }

                        float rx = dx * touchMoveFator;
                        float ry = dy * touchMoveFator;

                        mShapeGroup.setAppendRotate(ry, rx, 0);
//                        Log.message(PREFIX,"RotateX:" + mShapeGroup.getRotateX());
//                        Log.message(PREFIX,"RotateY:" + mShapeGroup.getRotateY());
                        mGLSurfaceView.requestRender();
                    }

                    mPreviousX = x;
                    mPreviousY = y;
                }
                break;
            }
            case MotionEvent.ACTION_UP:
                if(mPreviousDistance > 0) stopAnimating();
            case MotionEvent.ACTION_CANCEL: {
                mPreviousDistance = -1.0f;
                mPreviousX = -1.0f;
                mPreviousY = -1.0f;
                break;
            }
            default :
            }
        }

        return true;
    }

    public void onDrawFrame(GL10 gl) {
//        Log.message(PREFIX, "onDrawFrame() - S");
        
        if(mImageLoadIndex < mImageResourceIDList.length) {
        	EarthImageInfo imgInfo = mImageLoadInfoList.get(mImageLoadIndex);
        	if (imgInfo.status == LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_LOADING) {
        		if (sRealImageBitmap != null) {
        			imgInfo.earthSphere.loadGLTexture(gl, sRealImageBitmap);
        			if (sRealImageBitmap != null) {
        				sRealImageBitmap.recycle();
        				sRealImageBitmap = null;
        			}
        			imgInfo.status = LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_COMPLETE;
        			
        			mImageLoadIndex++;	// next image;
        		} else {
        			imgInfo.status = LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_START;
        		}
        	} 
        }
        
        gl.glDisable(GL10.GL_DITHER);
        gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
                GL10.GL_MODULATE);

        // Select the modelview matrix
        gl.glMatrixMode(GL10.GL_MODELVIEW);// OpenGL docs.
        // Reset the modelview matrix
        gl.glLoadIdentity();// OpenGL docs.

        // Clears the screen and depth buffer.
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        GLU.gluLookAt(gl,
                0, 0, INIT_CAMERA_LOACTION_Z_COORDINATE,	// CAMERA 
                0, 0, 0, 	// EYE 
                0, 1, 0); 	// UP VECTOR

        mTargetX = mShapeGroup.getRotateX();
        mTargetY = mShapeGroup.getRotateY();
        
        // shape draw
        mShapeGroup.draw(gl);
        
        
        int displayLevelMode = CITY_DISPLAY_LEVEL_NONE;
        float currentScale = getCurrentScale();
        
        if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_7) {
            displayLevelMode = CITY_DISPLAY_LEVEL_7;
        } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_6) {
            displayLevelMode = CITY_DISPLAY_LEVEL_6;
        } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_5) {
            displayLevelMode = CITY_DISPLAY_LEVEL_5;
        } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_4) {
            displayLevelMode = CITY_DISPLAY_LEVEL_4;
        } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_3) {
            displayLevelMode = CITY_DISPLAY_LEVEL_3;
        } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_2) {
            displayLevelMode = CITY_DISPLAY_LEVEL_2;
        } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_1) {
            displayLevelMode = CITY_DISPLAY_LEVEL_1;
        } else {
            displayLevelMode = CITY_DISPLAY_LEVEL_NONE;
        }
        
        if (displayLevelMode != CITY_DISPLAY_LEVEL_NONE) {
            mCityInfoDisplay.initDisplayCityInfoList();
            
            int cityCount = mCityInfoDisplay.getCityCount();
            
            float displayLimit = DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_1;
            
            if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_5) {
                displayLimit = DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_5;
            } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_4) {
                displayLimit = DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_4;
            } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_3) {
                displayLimit = DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_3;
            } else if (currentScale >= DISPLAY_SHAPE_SCALE_LEVEL_2) {
                displayLimit = DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_2;
            } else {
                displayLimit = DISPLAY_CITY_CULL_FACE_LIMIT_VALUE_1;
            }
            
            mProjector.currentModelView(gl);
            mLabelMaker.beginDrawing(gl, mViewWidth, mViewHeight);
            
            for (int i=0 ; i < cityCount ; i++) {
                CityInformation cityInfo = mCityInfoDisplay.getCityInformation(i);
                int displayLevel = cityInfo.getDisplayLevel();
                if (displayLevel == 0) continue;
                
                boolean isVisible = displayLevel <= displayLevelMode ? true : false;

                drawLabel(gl, cityInfo.getX(), cityInfo.getY(), cityInfo.getZ(), mLabels[i],
                        isVisible, cityInfo, displayLimit);
            }
            mLabelMaker.endDrawing(gl);
//            Log.message(PREFIX, "onDrawFrame() - label settings - E");
        }
        if(mImageLoadIndex < mImageResourceIDList.length) {
        	EarthImageInfo imgInfo = mImageLoadInfoList.get(mImageLoadIndex);
        	if (imgInfo.status == LOAD_REAL_IMAGE_PROCESS_STATE_NONE) {
        		Log.message(PREFIX, "Image Load Start - " + mImageLoadIndex);
        		imgInfo.status = LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_START;
        		mHandler.sendMessage(mHandler.obtainMessage(HANDLE_MESSAGE_TYPE_LOAD_IMAGE, this));
        	}
        }
    }
    
    private void drawLabel(GL10 gl, float x, float y, float z, int labelId, 
            boolean isVisible, 
            CityInformation cityInfo,
            float displayLimit) {
        if (!isVisible) return;

        mScratch[0] = x;
        mScratch[1] = y;
        mScratch[2] = z; //0.0f;
        mScratch[3] = 1.0f;

        mProjector.project(mScratch, 0, mScratch, 4);
        float sx = mScratch[4];
        float sy = mScratch[5];

        if (mScratch[4] < 15.0f || mScratch[4] > mViewWidth - 15) return;
        if (mScratch[5] < 25.0f || mScratch[5] > mViewHeight - 25) return;
        if (mScratch[6] > displayLimit) return;

        cityInfo.setWindowRect(mLabelMaker.getTextStartX(sx, labelId), 
        		mLabelMaker.getTextStartY((float)mViewHeight - sy, labelId), 
        		mLabelMaker.getTextEndX(sx, labelId), 
        		mLabelMaker.getTextEndY((float)mViewHeight - sy, labelId));
        
        cityInfo.setWindowX(sx + LABEL_LOCATION_ERROR_VALUE_X);
        cityInfo.setWindowY((float)mViewHeight - sy + LABEL_LOCATION_ERROR_VALUE_Y);
        
        mCityInfoDisplay.addDisplayCityInfo(cityInfo);
        mLabelMaker.draw(gl, sx, sy, labelId);
    }

    public void processZoomIn() {
        processScale(0.1f);
    }
    
    public void processZoomOut() {
        processScale(-0.1f);
    }
    
    private void processScale(float scale) {
        mShapeGroup.setAppendScale(scale);
        sCurrentScaleValue = mShapeGroup.getScale();
        
        mGLSurfaceView.requestRender();
    }

    public void zoomInEarth(GLSurfaceView surfaceView) {
        int currentZoomLevel = findCurrentZoomLevel(ZOOM_IN_STATUS);
        if(currentZoomLevel >= 0)
            changeZoomLevel(surfaceView, currentZoomLevel + 1);
    }
    
    public void zoomOutEarth(GLSurfaceView surfaceView) {
        int currentZoomLevel = findCurrentZoomLevel(ZOOM_OUT_STATUS);
        if(currentZoomLevel >= 0)
            changeZoomLevel(surfaceView, currentZoomLevel - 1);
    }
    
    private int findCurrentZoomLevel(int zoomStatus) {
        float currentScale = getCurrentScale();
        int retZoomLevel = -1;
        
        for(int i = 0; i < scaleArray.length; i++) {
            
            if(zoomStatus == ZOOM_OUT_STATUS) {
                if(currentScale > scaleArray[i]) {
                    retZoomLevel = i + 1;
                }
            }
            else {
                int index = (scaleArray.length-1)-i;
                if(currentScale < scaleArray[index]) {
                    retZoomLevel = index - 1;
                }
            }
        }
        
        return retZoomLevel;
    }

    private void changeZoomLevel(GLSurfaceView surfaceView, int zoomLevel) {
        float currentScale = getCurrentScale();
        float targetScale = scaleArray[zoomLevel];
        float scaleInterval = 0.0f;
        
        if(currentScale <= targetScale) {
            scaleInterval = 0.01f;
            while(currentScale <= targetScale) {
                mShapeGroup.setAppendScale(scaleInterval);
                surfaceView.requestRender();
                currentScale += scaleInterval;
                /*
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                */
            }
        }
        else {
            scaleInterval = -0.01f;
            while(currentScale >= targetScale) {
                mShapeGroup.setAppendScale(scaleInterval);
                surfaceView.requestRender();
                currentScale += scaleInterval;
                /*
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                */
            }
        }
        
        mShapeGroup.setScale(targetScale);
        sCurrentScaleValue = mShapeGroup.getScale();
        surfaceView.requestRender();
    }
    
    public void startAnimating(AnimationInfo aInfo) {
        mHandler.obtainMessage(HANDLE_MESSAGE_TYPE_ROTATE_ANIMATION, aInfo).sendToTarget();
    }

    /**
     * Quit animation.
     */
    public void stopAnimating() {
        mHandler.removeMessages(HANDLE_MESSAGE_TYPE_ROTATE_ANIMATION);
    }
    
    public void onDestroy() {
        Log.message(PREFIX, "onDestory() - S");
        if (sRealImageBitmap != null) {
            Log.message(PREFIX, "onDestroy() image recycle");
            sRealImageBitmap.recycle();
            sRealImageBitmap = null;
        }
        
        if (mLabelMaker != null) {
            Log.message(PREFIX, "onDestroy() label clear");
            mLabelMaker.clear();
        }
        mIsDestroy = true;
        
        Log.message(PREFIX, "onDestory() - E");
    }
    
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case HANDLE_MESSAGE_TYPE_ROTATE_ANIMATION:
                    if(msg.obj != null) {
                        AnimationInfo animationInfo = (AnimationInfo)msg.obj;
                        
                        int loopCount = animationInfo.getLoopCnt();
                        float velocityX = animationInfo.getVelocityX();
                        float velocityY = animationInfo.getVelocityY();
                        
                        if(loopCount > 0) {
                            float stepX = velocityX /(9000*getCurrentScale()) * loopCount; // * 1.5f; 
                            float stepY = velocityY /(9000*getCurrentScale()) * loopCount; // * 1.5f;
                            
                            mShapeGroup.setAppendRotate(stepY, stepX, 0);
                            mGLSurfaceView.requestRender();

                            animationInfo.setLoopCnt(loopCount - 2);
                            mHandler.sendMessageDelayed(
                                    mHandler.obtainMessage(HANDLE_MESSAGE_TYPE_ROTATE_ANIMATION, animationInfo),
                                    30);
                        }
                    }
                    break;
                case HANDLE_MESSAGE_TYPE_INIT_ROTATE:
                    if(msg.obj != null) {
                        AnimationInfo animationInfo = (AnimationInfo)msg.obj;
                        
                        int loopCount = animationInfo.getLoopCnt();
                        float stepX = animationInfo.getStepX();
                        float stepY = animationInfo.getStepY();
                        float stepScale = animationInfo.getStepScale();
                        Log.message("HANDLE_MESSAGE_TYPE_INIT_ROTATE");
                        if(loopCount > 0) {
        
                            mShapeGroup.setAppendRotate(stepX*3, stepY*3, 0);
                            mShapeGroup.setAppendScale(stepScale*3);
                            mGLSurfaceView.requestRender();

                            animationInfo.setLoopCnt(loopCount - 3);
                            mHandler.sendMessageDelayed(
                                    mHandler.obtainMessage(HANDLE_MESSAGE_TYPE_INIT_ROTATE, animationInfo),30);
                        } else {
                            sCurrentScaleValue = mShapeGroup.getScale();
                            mGLSurfaceView.requestRender();
                        }
                        
                    }
                    break;
                case HANDLE_MESSAGE_TYPE_LOAD_IMAGE:
                	if(msg.obj != null) {
                		EarthImageInfo imgInfo = mImageLoadInfoList.get(mImageLoadIndex);
                		if (imgInfo.status == LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_START) {
                			new ImageLoadingAsyncTask((EarthRenderer)msg.obj).execute(imgInfo);
                		}
                	} 
                	break;
                default :
            }
        }
    };
    
    /**
     * @author lge.com
     */
    public class ImageLoadingAsyncTask extends AsyncTask<EarthImageInfo, Void, Integer> {
        private EarthRenderer mRenderer;
        
        public ImageLoadingAsyncTask(EarthRenderer renderer) {
            mRenderer = renderer;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
        
        @Override
        protected Integer doInBackground(EarthImageInfo... params) {
            Log.message(PREFIX, "doInBackground() - S");
            EarthImageInfo imageInfo = null;
            
            //BASICPKG_S AlarmClock [hyeongjoo.park@lge.com 110314] create BitmapFactoryOption
            BitmapFactory.Options option = new BitmapFactory.Options();
            //BASICPKG_E AlarmClock
            
            /*[START] 2011.10.06 suein1209.kim @blocking the 3D map enter and leave button cuz out of memory*/
            
            // [mo2sangbong.lee@lge.com][Self test] 2011.08.12. 3D world clock the equator line issue fix START
//            if(AlarmClockActivity.bXhdpi){
//                option.inSampleSize = 2;
//            } else {
//                option.inSampleSize = 1;
//            }
            // [mo2sangbong.lee@lge.com][Self test] 2011.08.12. 3D world clock the equator line issue fix END

            /*image contrast*/
            option.inSampleSize = 2;
            
            /*memory guard when image is loaded*/
            option.inPurgeable = true;
            
            /*[START] 2011.10.06 suein1209.kim @blocking the 3D map enter and leave button cuz out of memory*/
            
            if (params != null) {
            	imageInfo = params[0];
                
                if (EarthRenderer.sRealImageBitmap != null) {
                    Log.message(PREFIX, "doInBackground() image recycle - 1");
                    EarthRenderer.sRealImageBitmap.recycle();
                    EarthRenderer.sRealImageBitmap = null;
                }
                
                if (mRenderer.mIsDestroy) {
                    Log.message(PREFIX, "doInBackground() finish - 1");
                    return -1;
                }
                
                if (imageInfo.status == LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_START) {
                	Log.message(PREFIX, "doInBackground() load - S");
                	
                	option.inPreferredConfig = Bitmap.Config.RGB_565; //[hyunjung78.park]20111124 https://medic.lge.com/issues/1953
                	
                	//BASICPKG_S AlarmClock [hyeongjoo.park@lge.com 110314] create BitmapFactoryOption
                	EarthRenderer.sRealImageBitmap = 
                				BitmapFactory.decodeResource(mRenderer.mContext.getResources(),
                														imageInfo.imageResourceID, option);
                	//BASICPKG_S AlarmClock
                	
                	if (mRenderer.mIsDestroy) {
                		Log.message(PREFIX, "doInBackground() finish - 3");
                		if (EarthRenderer.sRealImageBitmap != null) {
                			Log.message(PREFIX, "doInBackground() image recycle - 2");
                			EarthRenderer.sRealImageBitmap.recycle();
                			EarthRenderer.sRealImageBitmap = null;
                		}
                		return -1;
                	}
                	GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, EarthRenderer.sRealImageBitmap, 0);
                	imageInfo.status = LOAD_REAL_IMAGE_PROCESS_STATE_LOAD_LOADING;
                	publishProgress();
                	Log.message(PREFIX, "doInBackground() load - E");
                } 
            }
        
            if(imageInfo == null) {
            	return -1;
            }
            Log.message(PREFIX, "doInBackground() - E");
            return imageInfo.status;
        }
        
        @Override
        protected void onProgressUpdate(Void... values) {
        	super.onProgressUpdate(values);
        }
        
        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            
            if (mRenderer != null) {
                if (mRenderer.mGLSurfaceView != null) {
                    mRenderer.mGLSurfaceView.requestRender();
                }
            }
            Log.message(PREFIX, "onPostExecute() - E");
        }
    }
    
    /**
     * @author lge.com
     */
    class EarthImageInfo {
    	public int status = LOAD_REAL_IMAGE_PROCESS_STATE_NONE;
    	public int initializeImageID;
    	public int imageResourceID;
    	public EarthSphere earthSphere;
    }
}
